﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace npunkliner.lib
{
    /// <summary>
    /// Sniffes Netbios Datagrams in dedicated thread
    /// Issues PacketInfo instances for each datagram
    /// </summary>
    public class SambaListener
    {
        #region Fields

        Socket listenerSocket;
        Thread listenerThread; 

        #endregion

        #region Public Contract

        /// <summary>
        /// Runs this instance.
        /// </summary>
        public void Run()
        {
            listenerSocket = InitListenerSocket();
            listenerThread = new Thread(WaitForIncomingMessages)
            {
                Name = "Samba Listener Thread"
            };

            listenerThread.Start();
            OnRan();
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {
            listenerThread.Abort();
            OnStopped();
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Helper for dedicated thread
        /// Catches incoming netbios datagrams and raises OnHostStatusUpdate event
        /// </summary>
        /// <param name="state">The state.</param>
        private void WaitForIncomingMessages(object state)
        {
            int receivedLength;
            byte[] packetData = new byte[1024];
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint tmpRemote = (EndPoint)sender;

            while (true)
            {
                receivedLength = listenerSocket.ReceiveFrom(packetData, ref tmpRemote);
                var packetInfo = new PacketInfo(packetData) { EndPoint = tmpRemote };

                if (packetInfo.IsValid)
                {
                    var evArgs = new HostStatusUpdateEventArgs
                    {
                        PacketInfo = packetInfo,
                        UpdateDate = DateTime.Now,
                    };

                    OnHostStatusUpdate(evArgs);
                }
            }
        }

        /// <summary>
        /// Inits the listener socket.
        /// </summary>
        /// <returns>Listener socket instance</returns>
        private Socket InitListenerSocket()
        {
            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            // Set socket options
            listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
            listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

            // Bind socket to Netbios Datagram Service's port
            EndPoint listenerEP = new IPEndPoint(IPAddress.Any, 138);
            listener.Bind(listenerEP);

            return listener;
        }

        #endregion

        #region Events
		
        public event EventHandler<HostStatusUpdateEventArgs> HostStatusUpdate;
        public event EventHandler Ran;
        public event EventHandler Stopped;

        protected virtual void OnHostStatusUpdate(HostStatusUpdateEventArgs evArgs)
        {
            var tmp = HostStatusUpdate;
            if (tmp != null)
                tmp(this, evArgs);
        }

        protected virtual void OnRan()
        {
            var tmp = Ran;
            if (tmp != null)
                tmp(this, EventArgs.Empty);
        }

        protected virtual void OnStopped()
        {
            var tmp = Stopped;
            if (tmp != null)
                tmp(this, EventArgs.Empty);
        }

	    #endregion    
    }

    /// <summary>
    /// Stores SambaListener.HostStatusUpdate event args
    /// </summary>
    public class HostStatusUpdateEventArgs : EventArgs
    {
        public PacketInfo PacketInfo { get; set; }
        public DateTime UpdateDate { get; set; }
    }
}